package net.turkishpt;

import java.util.ArrayList;
import java.util.Arrays;

import net.zemberek.islemler.KelimeKokFrekansKiyaslayici;
import net.zemberek.tr.yapi.TurkiyeTurkcesi;
import net.zemberek.yapi.Alfabe;
import net.zemberek.yapi.Kelime;
import net.zemberek.yapi.Kok;

/**
 * Engine of text prediction for Turkish. Creates possible words from
 * keystroke combinations and then checks for their validity in 
 * Turkish grammar.
 * @author alp
 *
 */
public class PredictionEngine {

	/**
	 * Returns letter combinations as of given keys which are pressed
	 * consecutively as an ArrayList of String type. 
	 * @param keys
	 * @return ArrayList of Strings which are word combinations.
	 */
	public static ArrayList<String> getCombinations(PhoneKey[] keys){
		char[][] keyStrokes = new char[keys.length][];
		
		for(int i = 0; i < keyStrokes.length; i++)
			keyStrokes[i] = PhoneKeyLetters.getKeyLetters(keys[i]);
		
		return getCombinations(keyStrokes);
	}
	
	/**
	 * Returns letter combinations as of given 2-d character arrays
	 * consecutively as an ArrayList of String type.
	 * @param keys
	 * @return ArrayList of Strings which are word combinations.
	 */
	public static ArrayList<String> getCombinations(char[][] keyStrokes){
		ArrayList<String> foundCombinations = new ArrayList<String>();
		findCombinations(keyStrokes, foundCombinations, false, -1);

		return foundCombinations;
	}
	
	
	/**
	 * Returns letter combinations which are filtered by <code>LetterSequenceFilter</code>
	 * at given level of filtering. 
	 * @param keys
	 * @param level
	 * @return ArrayList of Strings which are word combinations.
	 */
	public static ArrayList<String> getFilteredCombinations(PhoneKey[] keys, int level){
		ArrayList<String> foundCombinations = new ArrayList<String>();
		
		char[][] keyStrokes = new char[keys.length][];
		
		for(int i = 0; i < keyStrokes.length; i++)
			keyStrokes[i] = PhoneKeyLetters.getKeyLetters(keys[i]);
		
		return findCombinations(keyStrokes, foundCombinations, true, level);
	}
	
	public static long getNumberOfCombinations(PhoneKey[] keys){
		long tmp = 1;
		
		for(int i = 0; i < keys.length; i++)
			tmp *= PhoneKeyLetters.getKeyLetters(keys[i]).length;
		
		return tmp;	
	}
	
	private static ArrayList<String> findCombinations(char[][] keyStrokes, ArrayList<String> foundCombinations, 
			boolean filtering, int filteringLevel){
		boolean found = false;
		int i = 0;
		while(i<keyStrokes.length){
			if(keyStrokes[i].length==1){
				i++; 
				if (i==keyStrokes.length){
					found = true;
					String tmp = charArrayToString(keyStrokes);
					if ((!filtering) || (filtering && LetterSequenceFilter.applyFilterLevel(tmp, filteringLevel))) 
						foundCombinations.add(tmp);
				}
			}
			else break;
		}

		if(!found){
			char[][] left, right;
			left = keyStrokes.clone();
			right = keyStrokes.clone();
				
			left[i] = new char[1];
			left[i][0] = keyStrokes[i][0];
				
			right[i] = shiftCharArray(right[i]);
			
			findCombinations(left, foundCombinations, filtering, filteringLevel); // recursive step
			findCombinations(right, foundCombinations, filtering, filteringLevel); // recursive step
		}
		
		return foundCombinations;
	}
	
	/**
	 * removes first element of a <code>char</code> array.
	 * @param a an array to shift its first element.
	 * @return modified (shifted) array.
	 */
	private static char[] shiftCharArray(char[] a){
		if (a.length <= 1) return new char[0];
		else {
			char[] tmp = new char[a.length - 1];
			for(int i = 1; i < a.length; i++){
				tmp[i-1] = a[i];
			}
			return tmp;
		}
	}

	/**
	 * dumps the character array to the screen for 
	 * debugging purposes
	 * @param a 2-dimension char array.
	 */
	private static void printCharArray(char[][] a){
		for(int i = 0; i < a.length; i++){
			
			for(int j = 0; j < a[i].length; j++){
				System.out.print(a[i][j]);
			}
			
			if (i != a.length-1 ) System.out.print(",");
		}
		System.out.println();
	}
	
	/**
	 * merges two 2-dimension char arrays to one array.
	 * @param a 2D char array which is going to appear first.
	 * @param b another 2D char array.
	 * @return merged 2D char array.
	 */
	private static char[][] mergeCharArray(char[][] a, char[][] b){
		char[] [] temp = new char[a.length+b.length][];
		
		for(int i = 0 ; i < a.length; i++)
			temp[i] = a[i];
		
		for(int i = 0; i < b.length; i++)
			temp[a.length + i] = b[i];
		
		return temp;
	}
	
	/**
	 * converts 2-dimension char arrays to a string by
	 * concatenation.
	 * @param a 2D char array
	 * @return concatenated string of 
	 */
	private static String charArrayToString(char[][] a){
		StringBuffer temp = new StringBuffer();
		
		for(int i = 0 ; i < a.length; i++)
			for(int j = 0; j < a[i].length ; j++)
				temp.append(a[i][j]);
	
		return temp.toString();
	}
	
	public static String[] sortResultsByFrequency(ArrayList<String> results){
		String[] sorted = new String[results.size()];
		
		for(int i = 0; i < results.size(); i++ )
			sorted[i] = results.get(i);
		
		Arrays.sort(sorted, new FrequencyComparator());
		
		return sorted;
	}
	
}